﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the Microsoft Public License (Ms-PL).
// See accompanying file License.txt or copy at http://mews.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Mews.Application
{
    using System;
    using System.Security.Cryptography;

    using Mews.Net.Nntp.Cache;

    /// <summary>Provides user-specific encryption.</summary>
    internal sealed class UserSpecificCryptoProvider : ICryptoProvider
    {
        /// <summary>See <see cref="ICryptoProvider.Encrypt"/>.</summary>
        public byte[] Encrypt(byte[] input)
        {
            return PerformCryptoOperation(
                input,
                (inputBlob, outputBlob) =>
                    NativeMethods.CryptProtectData(inputBlob, null, null, IntPtr.Zero, IntPtr.Zero, 0, outputBlob));
        }

        /// <summary>See <see cref="ICryptoProvider.Decrypt"/>.</summary>
        public byte[] Decrypt(byte[] input)
        {
            return PerformCryptoOperation(
                input,
                (inputBlob, outputBlob) =>
                    NativeMethods.CryptUnprotectData(inputBlob, null, null, IntPtr.Zero, IntPtr.Zero, 0, outputBlob));
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private static byte[] PerformCryptoOperation(
            byte[] input, Func<NativeMethods.DATA_BLOB, NativeMethods.DATA_BLOB, bool> operation)
        {
            if (input == null)
            {
                return input;
            }

            using (NativeMethods.DATA_BLOB inputBlob = new NativeMethods.DATA_BLOB(input))
            using (NativeMethods.DATA_BLOB outputBlob = new NativeMethods.DATA_BLOB())
            {
                if (!operation(inputBlob, outputBlob))
                {
                    throw new CryptographicException("Cryptographic operation failed.");
                }

                return outputBlob.Data;
            }
        }
    }
}
